package org.micro.frame.service.jackson;

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.ser.BeanPropertyWriter;
import com.fasterxml.jackson.databind.ser.BeanSerializerModifier;
import com.fasterxml.jackson.databind.ser.SerializerFactory;

import java.io.IOException;
import java.util.Collections;
import java.util.List;

/**
 * The Jackson SerializerFactory
 *
 * @author lry
 */
public class JacksonSerializerFactory extends BeanSerializerModifier {

    private final JsonSerializer<Object> nullStringJsonSerializer;
    private final JsonSerializer<Object> nullNumberJsonSerializer;
    private final JsonSerializer<Object> nullBooleanJsonSerializer;
    private final JsonSerializer<Object> nullListJsonSerializer;
    private final JsonSerializer<Object> nullMapJsonSerializer;

    private JacksonSerializerFactory() {
        nullStringJsonSerializer = new NullStringSerializer();
        nullNumberJsonSerializer = new NullNumberSerializer();
        nullBooleanJsonSerializer = new NullBooleanSerializer();
        nullListJsonSerializer = new NullListJsonSerializer();
        nullMapJsonSerializer = new NullMapSerializer();
    }

    public static void wrapperSerializerFactory(ObjectMapper objectMapper) {
        SerializerFactory serializerFactory = objectMapper.getSerializerFactory();
        serializerFactory = serializerFactory.withSerializerModifier(new JacksonSerializerFactory());
        objectMapper.setSerializerFactory(serializerFactory);
    }

    @Override
    public List<BeanPropertyWriter> changeProperties(
            SerializationConfig config, BeanDescription beanDesc, List<BeanPropertyWriter> beanProperties) {
        for (BeanPropertyWriter writer : beanProperties) {
            final JavaType javaType = writer.getType();
            final Class<?> rawClass = javaType.getRawClass();

            if (String.class.equals(rawClass)) {
                writer.assignNullSerializer(nullStringJsonSerializer);
            } else if (Number.class.isAssignableFrom(rawClass)) {
                writer.assignNullSerializer(nullNumberJsonSerializer);
            } else if (Boolean.class.equals(rawClass)) {
                writer.assignNullSerializer(nullBooleanJsonSerializer);
            } else if (javaType.isArrayType() || javaType.isCollectionLikeType()) {
                writer.assignNullSerializer(nullListJsonSerializer);
            } else if (javaType.isMapLikeType()) {
                writer.assignNullSerializer(nullMapJsonSerializer);
            }
        }

        return super.changeProperties(config, beanDesc, beanProperties);
    }

    private static class NullListJsonSerializer extends JsonSerializer<Object> {
        @Override
        public void serialize(Object value, JsonGenerator jgen, SerializerProvider provider) throws IOException {
            jgen.writeObject(Collections.emptyList());
        }
    }

    private static class NullNumberSerializer extends JsonSerializer<Object> {
        @Override
        public void serialize(Object value, JsonGenerator jgen, SerializerProvider provider) throws IOException {
            jgen.writeNumber(0);
        }
    }

    private static class NullBooleanSerializer extends JsonSerializer<Object> {
        @Override
        public void serialize(Object value, JsonGenerator jgen, SerializerProvider provider) throws IOException {
            jgen.writeBoolean(false);
        }
    }

    private static class NullStringSerializer extends JsonSerializer<Object> {
        @Override
        public void serialize(Object value, JsonGenerator jgen, SerializerProvider provider) throws IOException {
            jgen.writeString("");
        }
    }

    private static class NullMapSerializer extends JsonSerializer<Object> {
        @Override
        public void serialize(Object value, JsonGenerator jgen, SerializerProvider provider) throws IOException {
            jgen.writeObject(Collections.emptyMap());
        }
    }

}
